// SPDX-License-Identifier: GPL-2.0
/*
 * MAX6675 SPI Thermocouple temperature sensor driver
 *
 * Copyright (C) 2024 Your Name
 */

/*
 * 该驱动依赖以下内核头文件：
 * - linux/module.h: 内核模块支持
 * - linux/init.h: 模块初始化
 * - linux/spi/spi.h: SPI总线支持
 * - linux/iio/iio.h: 工业I/O子系统
 * - linux/iio/sysfs.h: IIO sysfs接口
 * - linux/device.h: Linux设备模型
 * - linux/pm.h: 电源管理
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/device.h>
#include <linux/pm.h>
#include "max6675.h"

// 温度单位转换函数
static inline int celsius_to_fahrenheit(int celsius)
{
    return (celsius * 9 / 5) + 32;
}

// sysfs属性处理函数
static ssize_t sample_interval_show(struct device *dev,
                                  struct device_attribute *attr,
                                  char *buf)
{
    struct iio_dev *indio_dev = dev_to_iio_dev(dev);
    struct max6675_data *data = iio_priv(indio_dev);

    return sprintf(buf, "%u\n", data->sample_interval);
}

static ssize_t sample_interval_store(struct device *dev,
                                   struct device_attribute *attr,
                                   const char *buf, size_t count)
{
    struct iio_dev *indio_dev = dev_to_iio_dev(dev);
    struct max6675_data *data = iio_priv(indio_dev);
    unsigned int val;
    int ret;

    ret = kstrtouint(buf, 10, &val);
    if (ret < 0)
        return ret;

    // 限制最小采样间隔为220ms(MAX6675转换时间)
    if (val < 220)
        return -EINVAL;

    mutex_lock(&data->lock);
    data->sample_interval = val;
    mutex_unlock(&data->lock);

    return count;
}

static ssize_t temperature_unit_show(struct device *dev,
                                   struct device_attribute *attr,
                                   char *buf)
{
    struct iio_dev *indio_dev = dev_to_iio_dev(dev);
    struct max6675_data *data = iio_priv(indio_dev);

    return sprintf(buf, "%s\n", data->celsius_mode ? "celsius" : "fahrenheit");
}

static ssize_t temperature_unit_store(struct device *dev,
                                    struct device_attribute *attr,
                                    const char *buf, size_t count)
{
    struct iio_dev *indio_dev = dev_to_iio_dev(dev);
    struct max6675_data *data = iio_priv(indio_dev);

    if (sysfs_streq(buf, "celsius"))
        data->celsius_mode = true;
    else if (sysfs_streq(buf, "fahrenheit"))
        data->celsius_mode = false;
    else
        return -EINVAL;

    return count;
}

static IIO_DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
                      sample_interval_show,
                      sample_interval_store, 0);

static IIO_DEVICE_ATTR(temperature_unit, S_IRUGO | S_IWUSR,
                      temperature_unit_show,
                      temperature_unit_store, 0);

static struct attribute *max6675_attributes[] = {
    &iio_dev_attr_sampling_frequency.dev_attr.attr,
    &iio_dev_attr_temperature_unit.dev_attr.attr,
    NULL,
};

static const struct attribute_group max6675_attribute_group = {
    .attrs = max6675_attributes,
};

static int max6675_read_raw(struct iio_dev *indio_dev,
                           struct iio_chan_spec const *chan,
                           int *val, int *val2, long mask)
{
    struct max6675_data *data = iio_priv(indio_dev);
    int ret;
    u16 value;
    u8 rx_buf[2] = {0};

    if (!data || !data->spi) {
        dev_err(&indio_dev->dev, "Invalid device data\n");
        return -EINVAL;
    }

    switch (mask) {
    case IIO_CHAN_INFO_RAW:
        mutex_lock(&data->lock);
        
        /* Start SPI transfer */
        ret = spi_read(data->spi, rx_buf, 2);
        if (ret < 0) {
            dev_err(&indio_dev->dev, "SPI read failed: %d\n", ret);
            mutex_unlock(&data->lock);
            return ret;
        }

        /* Convert received data */
        value = (rx_buf[0] << 8) | rx_buf[1];
        
        max6675_dbg(&indio_dev->dev, "Raw SPI data: 0x%04x\n", value);

        /* Validate data */
        if (value == 0 || value == 0xFFFF) {
            dev_err(&indio_dev->dev, "Invalid data received: 0x%04x\n", value);
            mutex_unlock(&data->lock);
            return -EIO;
        }
        
        /* Check if thermocouple is open */
        if (value & MAX6675_ERR_THERMOCOUPLE_NOT_CONNECTED) {
            dev_err(&indio_dev->dev, "Thermocouple not connected\n");
            mutex_unlock(&data->lock);
            return -ENODEV;
        }

        /* Temperature is in the upper 12 bits, LSB = 0.25 degree Celsius */
        value >>= 3;
        
        /* Validate temperature range (0°C to +1024°C) */
        if (value > 4096) {
            dev_err(&indio_dev->dev, "Temperature out of range: %d\n", value);
            mutex_unlock(&data->lock);
            return -ERANGE;
        }

        data->last_value = value;
        max6675_dbg(&indio_dev->dev, "Temperature value: %d (0.25°C)\n", value);
        
        mutex_unlock(&data->lock);

        if (data->celsius_mode) {
            *val = value;
            *val2 = 250000; /* 0.25 degrees Celsius */
        } else {
            *val = celsius_to_fahrenheit(value);
            *val2 = 250000; /* 0.25 degrees Fahrenheit */
        }
        return IIO_VAL_FRACTIONAL;

    default:
        return -EINVAL;
    }
}

static const struct iio_chan_spec max6675_channels[] = {
    {
        .type = IIO_TEMP,
        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
    }
};

static const struct iio_info max6675_info = {
    .read_raw = max6675_read_raw,
};

static int max6675_probe(struct spi_device *spi)
{
    struct iio_dev *indio_dev;
    struct max6675_data *data;
    int ret;

    if (!spi) {
        dev_err(&spi->dev, "Invalid SPI device\n");
        return -EINVAL;
    }

    indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
    if (!indio_dev) {
        dev_err(&spi->dev, "Failed to allocate IIO device\n");
        return -ENOMEM;
    }

    data = iio_priv(indio_dev);
    data->spi = spi;
    mutex_init(&data->lock);

    // 配置SPI
    spi->mode = SPI_MODE_0;
    spi->bits_per_word = 8;
    ret = spi_setup(spi);
    if (ret < 0) {
        dev_err(&spi->dev, "Failed to setup SPI: %d\n", ret);
        return ret;
    }

    // 测试设备是否存在
    u8 test_buf[2];
    ret = spi_read(spi, test_buf, 2);
    if (ret < 0) {
        dev_err(&spi->dev, "Device not responding: %d\n", ret);
        return ret;
    }

    indio_dev->name = DRIVER_NAME;
    indio_dev->info = &max6675_info;
    indio_dev->channels = max6675_channels;
    indio_dev->num_channels = ARRAY_SIZE(max6675_channels);
    indio_dev->modes = INDIO_DIRECT_MODE;

    // 初始化默认值
    data->sample_interval = 1000; // 默认1秒
    data->celsius_mode = true;    // 默认摄氏度

    ret = devm_iio_device_register_with_groups(&spi->dev, indio_dev,
                                              (const struct attribute_group *[]) {
                                                  &max6675_attribute_group,
                                                  NULL
                                              });
    if (ret) {
        dev_err(&spi->dev, "Failed to register device with groups: %d\n", ret);
        return ret;
    }

    dev_info(&spi->dev, "MAX6675 temperature sensor initialized\n");
    return 0;
}

static const struct of_device_id max6675_dt_ids[] = {
    { .compatible = "maxim,max6675" },
    { }
};
MODULE_DEVICE_TABLE(of, max6675_dt_ids);

static int max6675_suspend(struct device *dev)
{
    struct spi_device *spi = to_spi_device(dev);
    struct iio_dev *indio_dev = spi_get_drvdata(spi);
    struct max6675_data *data = iio_priv(indio_dev);

    mutex_lock(&data->lock);
    // MAX6675是一个简单的设备，没有特殊的休眠模式
    // 这里我们只需要确保没有正在进行的操作
    mutex_unlock(&data->lock);

    dev_dbg(dev, "MAX6675 suspended\n");
    return 0;
}

static int max6675_resume(struct device *dev)
{
    struct spi_device *spi = to_spi_device(dev);
    struct iio_dev *indio_dev = spi_get_drvdata(spi);
    struct max6675_data *data = iio_priv(indio_dev);
    int ret;

    mutex_lock(&data->lock);
    // 重新初始化SPI配置
    spi->mode = SPI_MODE_0;
    spi->bits_per_word = 8;
    ret = spi_setup(spi);
    if (ret < 0) {
        dev_err(dev, "Failed to setup SPI after resume: %d\n", ret);
        mutex_unlock(&data->lock);
        return ret;
    }
    mutex_unlock(&data->lock);

    dev_dbg(dev, "MAX6675 resumed\n");
    return 0;
}

static const struct dev_pm_ops max6675_pm_ops = {
    .suspend = max6675_suspend,
    .resume = max6675_resume,
};

static struct spi_driver max6675_driver = {
    .driver = {
        .name = DRIVER_NAME,
        .of_match_table = max6675_dt_ids,
        .pm = &max6675_pm_ops,  // 添加电源管理操作
    },
    .probe = max6675_probe,
};

module_spi_driver(max6675_driver);

MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("MAX6675 SPI Thermocouple temperature sensor driver");
MODULE_LICENSE("GPL v2"); 